#!/bin/bash

#
## Global Environment Setting
#

shopt -s expand_aliases
shopt -s shift_verbose
shopt -s xpg_echo
shopt -s sourcepath
shopt -s extglob
shopt -s globstar

#
## Global Private Environment Variable
#
declare -g __NUTS_ALMOND_PATH="$(cd "${BASH_SOURCE[0]%/*}" && pwd)"
declare -g __NUTS_ALMOND_LIB="${__NUTS_ALMOND_PATH}/lib"
declare -g __NUTS_ALMOND_TASK="${__NUTS_ALMOND_PATH}/task"
declare -g __NUTS_ALMOND_TEMP="${__NUTS_ALMOND_PATH}/.temporary"

declare -g __NUTS_ALMOND_STDOUT='NUTS_:> '

#
## Global Public Environment Variable
#
declare -g NUTS_ALMOND_VERSION='Nuts-Almond V0.0.1'
declare -g NUTS_ALMOND_LOG_LEVEL=info # logging level: debug,info,warn,error,fatal

[[ ! -e ${__NUTS_ALMOND_TEMP} ]] && mkdir -p ${__NUTS_ALMOND_TEMP}

#
## Global Function
#

#
## 获取日志级别，若函数级别高于环境配置级别，则输出函数级别；若函数级别低于环境配置级别，则不输出日志级别
#
system::logger::getLevel() {
    local level=${1:?"[${FUNCNAME}] 需要日志类型参数！"'$1:level 日志级别' }

    local logLevels="debug info warn error fatal"
    for lvl in $NUTS_ALMOND_LOG_LEVEL${logLevels##*$NUTS_ALMOND_LOG_LEVEL}; do
        if [[ $lvl == $level ]]; then
            echo $lvl
        fi
    done

    echo ''
}

#
## 默认日志函数
#
system::logger::logging() {
    local level=${1:?"[${FUNCNAME}] 需要日志级别参数！"'$1:level 日志级别' }
    local functionName=${2:?"[${FUNCNAME}] 需要调用函数参数！"'$2:functionName 调用函数' }
    local message=${3:?"[${FUNCNAME}] 需要日志消息参数！"'$2:message 日志消息' }

    local color_stdout="\033[32m$__NUTS_ALMOND_STDOUT\033[0m"
    local message_level=$(echo "[${level:0:6}] " | tr 'a-z' 'A-Z')
    shift 2
    message="$@"
    local message_base='['$(date +%X)']'" ${message_level:0:7} - ${BASH_SOURCE} -> ${functionName} : ${message}"

    local message_debug=$color_stdout"\033[34m${message_base}\033[0m"
    local message_info=$color_stdout"${message_base}"
    local message_warn=$color_stdout"\033[33m${message_base}\033[0m"
    local message_error=$color_stdout"\033[31m${message_base}\033[0m"
    local message_fatal=$color_stdout"\033[35m${message_base}\033[0m"

    declare format=""
    case ${level} in
    debug)
        [[ -n $(system::logger::getLevel debug) ]] && format="${message_debug}"
        ;;
    info)
        [[ -n $(system::logger::getLevel info) ]] && format="${message_info}"
        ;;
    warn)
        [[ -n $(system::logger::getLevel warn) ]] && format="${message_warn}"
        ;;
    error)
        [[ -n $(system::logger::getLevel error) ]] && format="${message_error}"
        ;;
    fatal)
        [[ -n $(system::logger::getLevel fatal) ]] && format="${message_fatal}"
        ;;
    *)
        [[ -n $(system::logger::getLevel info) ]] && format="${message_info}"
        ;;
    esac
    shift 2

    [[ -n ${format} ]] && printf "${format}\n" '' || printf "" ''
}

#
## Global Logger Alias
#
alias logger::logging="system::logger::logging"
alias logger::debug="system::logger::logging debug"
alias logger::info="system::logger::logging info "
alias logger::warn="system::logger::logging warn"
alias logger::error="system::logger::logging error"
alias logger::fatal="system::logger::logging fatal"

#
## 检查变量名称是否合法
#
system::filterVariableChar() {
    local varName=${1:?"[${FUNCNAME}] 需要变量名称参数！"'$1:varName 变量名称' }

    echo $varName | sed 's/[A-Za-z]//g' | sed 's/[0-9]//g' | sed 's/_//g'
}

#
## 获取文件的绝对路径
#
system::getAbsolutePath() {
    local filePath=${1:?"[${FUNCNAME}] 需要文件的路径参数！"'$1:filePath SHELL文件的绝对路径/相对路径' }

    if [[ "$filePath" == "/"* ]]; then
        echo "$filePath"
    else
        echo "$(cd "$(dirname "$filePath")" && pwd)/$(basename "$filePath")"
    fi
}

#
## Wrap外部SHELL脚本文件,文件的路径作为参数，按照Function的参数传入
#
system::wrapSource() {
    local libPath=${1:?"[${FUNCNAME}] 需要wrap的Shell文件的路径！"'$1:filePath SHELL文件的绝对路径/相对路径' }
    shift

    logger::info ${FUNCNAME} "开始wrap外部shell文件："$libPath
    builtin source "$libPath" "$@" ||
        logger::error ${FUNCNAME} "不能wrap外部shell文件："$libPath
}

#
## Source外部SHELL脚本文件,文件的路径作为参数，按照Function的参数传入
#
system::sourceFile() {
    local libPath=${1:?"[${FUNCNAME}] 需要source的Shell文件的路径！"'$1:filePath SHELL文件的绝对路径/相对路径' }
    shift

    logger::debug ${FUNCNAME} "检查是否存在外部shell文件："$libPath
    [[ ! -f "$libPath" ]] && [[ ! -f "$libPath.sh" ]] && return 1

    libPath="$(system::getAbsolutePath "$libPath")"
    [[ ! -f "$libPath" ]] && libPath="$(system::getAbsolutePath "$libPath.sh")"

    if [[ -f "$libPath" ]]; then
        #若已经import，则不需哟重新import
        if [[ -z $libPath ]]; then
            logger::info ${FUNCNAME} "已经Source外部shell文件："$libPath
        else
            logger::info ${FUNCNAME} "开始Source外部shell文件："$libPath

            system::wrapSource "$libPath" "$@"
        fi
    else
        logger::error ${FUNCNAME} "不能Source外部shell文件："$libPath
    fi
}

#
## 引用单个外部SHELL脚本文件,文件的路径作为参数，按照Function的参数传入
#
system::importOne() {
    local libPath=${1:?"[${FUNCNAME}] 需要import的Shell文件的路径！"'$1:filePath SHELL文件的绝对路径/相对路径' }
    shift

    logger::info ${FUNCNAME} "开始import外部shell文件："$libPath
    # try relative to parent script
    # try with parent
    # try without parent
    # try global library
    # try local library
    system::sourceFile "${libPath}" "$@" ||
        {
            local localPath="$(cd "${BASH_SOURCE%/*}" && pwd)"
            localPath="${localPath}/${libPath}"
            system::sourceFile "${localPath}" "$@"
        } ||
        system::sourceFile "${__NUTS_ALMOND_LIB}/${libPath}" "$@" ||
        system::sourceFile "${__NUTS_ALMOND_PATH}/${libPath}" "$@" ||
        logger::error ${FUNCNAME} "不能import外部shell文件：${libPath}"
}

#
## 引用外部SHELL脚本文件,若引用多个文件，每个文件的路径作为一个参数，按照Function的多参数传入
#
system::import() {
    : ${1:?"[${FUNCNAME}] 至少需要一个参数！"'$1:filePaths SHELL文件的绝对路径/相对路径' }

    local libPath
    for libPath in "$@"; do
        logger::info ${FUNCNAME} "开始import外部shell文件："$libPath
        system::importOne "$libPath"
    done
}

##################################################################################
###                              INITIALZE SYSTEM                              ###
##################################################################################
NUTS_ALMOND_LOG_LEVEL=debug
logger::info global::process "initialize system..."

system::import lib/arrayList lib/arrayHash

system::import test/array-hash
# system::import test/array-test
